#!/usr/bin/perl
use warnings;
use strict;




#use Env     qw(USER); 
#use Data::Dumper::Concise;

use feature qw/say/;  

use Carp            qw(confess     cluck);       
use Cwd             qw{abs_path    cwd};         
use File::Basename;                              
use File::Path      qw(make_path);               
use FindBin         qw($RealBin    $RealScript); 
use Getopt::Long;                                

use lib $RealBin;



use CON;                                                                                                 
use Carp::Always::Color;                                                                                 
use Data::Printer            {indent        =>                   1};                                     
use Math::Interpolate        qw(derivatives constant_interpolate linear_interpolate robust_interpolate); 
#use Statistics::Descriptive;                                                                             
use ZipSlurp;                                                                                            
use FlHandle;
#use JSON::MaybeXS;
use YAML;

$|=1;



######################
#  Define Variables  #
######################

my $OutDir                                = undef;
my $check_power                           = undef;
my $flg_TimingModePresence                = undef;
my $idx1                                  = 20.0;
my $idx2                                  = 20.0;
my $idx3                                  = 20.0;
my $opt_check_lvf_ocv_token               = 0;
my $opt_clock_underscore_number_collpased = 0;
my $opt_csv                               = 1;
my $opt_debug_yaml                        = 1;
my $opt_debug_ddp                         = 0;

my  @ALibFs;
my  @AIndex;

##################
#  Command Line  #
##################

&GetOptions(
    "index=f{,}"     => \@AIndex,
    "libs=s{,}"      => \@ALibFs,
    'collapse_clock' => \$opt_clock_underscore_number_collpased,
    'csv'            => \$opt_csv,
    'ddp_debug!'     => \$opt_debug_ddp,
    'lvf|ocv'        => \$opt_check_lvf_ocv_token,
    'out_dir=s'      => \$OutDir,
    'power'          => \$check_power,
    'yaml_out!'      => \$opt_debug_yaml,
);


if(not @ALibFs )  { 
    print_usage();
    Carp::confess "-E-no -libs provided.";
}
if (@AIndex){ $idx1 = shift @AIndex; }
if (@AIndex){ $idx2 = shift @AIndex; }
if (@AIndex){ $idx3 = shift @AIndex; }

map { if (!-e $_) { Carp::confess "-E- lib file $_ doesn't exist; please remove it from arguments"} } @ALibFs;

my %HLb20ww36d114_6dd52191e5;
my %HFBase;


 my @AabsLFs;
 my $joint_file_base = "";
foreach my $libfi (@ALibFs){
    my $abs_libfi = abs_path($libfi) or Cwd::fast_abs_path($libfi);
    if ($HLb20ww36d114_6dd52191e5{$abs_libfi}) {
        next;
    } else { $HLb20ww36d114_6dd52191e5{$abs_libfi} = 1; 
        push @AabsLFs , $abs_libfi;
        my ($fbase,$fd,$fsuff) = fileparse($abs_libfi, qr/\.[^.]*(\.(?:gz|bz2))?/);
        if ($HFBase{$fbase}){;} 
        else {
            if ( length($joint_file_base) < 60 ) { 
                $joint_file_base .= $fbase.".";
            }
        }
    }
}

my $yywwString =  &FlHandle::GetYWStr;
$yywwString .=  "_";
$yywwString .=  &FlHandle::GetRndHex;
$joint_file_base .= qq/$yywwString./;



if($OutDir  and (!-d $OutDir)) { 
    &make_path ($OutDir) or Carp::confess "cannot mkdir $OutDir";
}
if (!$OutDir){
    $OutDir = "./";
}
$OutDir = abs_path($OutDir);

# pre-compiled patterns 
# #vim_folder_{{{
my $parenth_name = qr { \s* \(\s* \S+ \s* \)\s*}six;
my $parenth_only = qr { \s* \(\s*  \)\s*}six;
my $braces = qr{ ( \s* \{ (?: (?> [^\{\}]+ ) | (?-1) )* \}) }six;
my $ws = qr{[ \t]*};
my $parentheses = qr{ ( \s* \( (?: (?> [^\(\)]+ ) | (?-1) )* \)) }six;


my $RiseFallPatterns = qr{\b (rise_constraint|fall_constraint|cell_rise|cell_fall ) ($parenth_name) $braces}six;

if ($check_power) {
    if ($opt_check_lvf_ocv_token) {
        $RiseFallPatterns = qr{\b (rise_constraint|fall_constraint|cell_rise|cell_fall|rise_power|fall_power|ocv_sigma_cell_fall|ocv_sigma_cell_rise|ocv_sigma_fall_transition|ocv_sigma_rise_transition) ($parenth_name) $braces}six;
    } else {
        $RiseFallPatterns = qr{\b (rise_constraint|fall_constraint|cell_rise|cell_fall|rise_power|fall_power ) ($parenth_name) $braces}six;
    }
} else {
    if ($opt_check_lvf_ocv_token) {
        $RiseFallPatterns = qr{\b (rise_constraint|fall_constraint|cell_rise|cell_fall|ocv_sigma_cell_fall|ocv_sigma_cell_rise|ocv_sigma_fall_transition|ocv_sigma_rise_transition) ($parenth_name) $braces}six;
    } else {
        ; #default
    }
}



my $ValuesPatterns = qr{values $parentheses}six;
my $TimingTypePattern = qr{timing_type $ws : $ws (\S+)}six;
#my $postfix =$$; $postfix .= "."; $postfix .=  substr(time(), -5);

my @Tassert = qw(rise_constraint fall_constraint cell_rise cell_fall );
if ($check_power)             { push @Tassert, qw( rise_power fall_power); }
if ($opt_check_lvf_ocv_token) {
    push @Tassert, qw(ocv_sigma_cell_fall ocv_sigma_cell_rise ocv_sigma_fall_transition ocv_sigma_rise_transition);
}

#vim_folder_}}}



my %HTlu;
my %HA;
my %HAmin;
my $rHintn={}; #internal pin
my $rHintn_min={}; #internal pin





my $fni = 0;
foreach my $fi ( @AabsLFs){
    printf "reading %s \n", &CONm( $fi, 2);
    &LibFile2Hash($fi, \%HA, \%HAmin, "file$fni", $rHintn, $rHintn_min);
    $fni ++;
}    


&PropagateThruInternalPin(\%HA,   $rHintn);
&PropagateThruInternalPin(\%HAmin,   $rHintn_min);


if ($opt_debug_ddp) {

    my $Dfile = "$OutDir/${joint_file_base}debug.txt.bz2";
#printf "-I- debug file : %s \n", &CONm( $Dfile, 4);
    my $HaString = np @AabsLFs;
    $HaString =~ s/^/# /sixmg;
    $HaString .= "\n\n";
    $HaString .= np %HA;
    $HaString .= "\n\n\n\n# then the min-delay related timing assertions \n\n";
    $HaString .= np %HAmin;
    &ZipSlurp::writeZF( \$HaString, $Dfile );
} else {
    # else body 
} # end of if-else on condition 1 

# the YAML format;
if ($opt_debug_yaml) {
    my $stk0 = &Dump( "used liberty files", \@AabsLFs, "timing-tokens", \%HA, "min-timing-tokens", \%HAmin );
    my $yfile = "$OutDir/${joint_file_base}debug.yaml.bz2";
    &ZipSlurp::writeZF( \$stk0, $yfile );
} else {
    # else body
}    # end of if-else on condition 1

my $libfile_count = $#AabsLFs;

if ($opt_csv){
    my $CsvF = "$OutDir/${joint_file_base}lib_sampling.csv";
    &Hash2Csv(\%HA,  $CsvF, $libfile_count);
    $CsvF = "$OutDir/${joint_file_base}minlib_sampling.csv";
    &Hash2Csv(\%HAmin,  $CsvF, $libfile_count);
}


#------------------------------------------------------
# 
##------------------------------------------------------
sub PropagateThruInternalPin
{#vim_folder_{{{
    my ( $rH, $rHi) = @_;
    my $rH2 = {};
    foreach my $k1 ( sort keys %{$rHi} ) {
        foreach my $k2 ( sort keys %{ $rHi->{$k1} } ) {
            my ( $rpin1, $TTy1, $pin1 ) = ( $k2 =~ m#( [^,]+) , ([^,-]+) -> (\S+) #six );
            if (not $rpin1){next;}
            $rH2->{$k1}{$pin1} = $k2;
#            print "### {"; print __FILE__; print "}-{"; print __LINE__; print "}, ($rpin1, $TTy1, $pin1)" . "\n";
        }
    }
    foreach my $k1 ( sort keys %{$rH} ) {
        foreach my $k2 ( sort keys %{ $rH->{$k1} } ) {
            my ( $rpin1, $TTy1, $pin1 ) = ( $k2 =~ m#( [^,]+) , ([^,-]+) -> (\S+) #six );
            if ( not $rpin1 ) { next; }
            if ( $rH2->{$k1}{$rpin1} ) { # found the rpin1 is actually an internal pin
                my $intk = $rH2->{$k1}{$rpin1};
                my ( $rpin1i, $TTy1i, $pin1i ) = ( $intk =~ m#( [^,]+) , ([^,-]+) -> (\S+) #six );
#                say "$intk ==> $k2";
                my $TTy2 = ( $TTy1 =~ m#combinational#i ) ? $TTy1i : $TTy1;
                my $k2c = qq($rpin1i,$TTy2->$pin1);
#                p $k2c;
                my $rH3i = $rHi->{$k1}{$intk};
                my $rH3  = $rH->{$k1}{$k2};
                foreach my $k3 ( sort keys %{$rH3} ) {
                    if ( $rH3i->{$k3} ) {    #both arcs have the same timing type, like cell_rise
                        my $rH4i = $rH3i->{$k3};
                        my $rH4  = $rH3->{$k3};
                        foreach my $k4 ( sort keys %{$rH4} ) {
                            my $val4a  = $rH4->{$k4}{value}  // 0;
                            my $val4b  = $rH4i->{$k4}{value} // 0;
                            my $val4c  = $val4a + $val4b;
                            my $indexc = $rH4->{$k4}{index}  // $rH4i->{$k4}{index};

#                            print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, $indexc; $val4c"."\n";
                            $rH->{$k1}{$k2c}{$k3}{$k4}{value} = $val4c;
                            $rH->{$k1}{$k2c}{$k3}{$k4}{index} = $indexc;
                        }
                    }
                }
                delete $rH->{$k1}{$k2};
            }
        }
    }

}#vim_folder_}}}

sub LibFile2Hash 
{#vim_folder_{{{

    my ($LibFin, $rH, $rHmin, $fileIndex, $rHi, $rHimin) = @_;

    my $libaa ;
    &ZipSlurp::read_text_zipped_okay($LibFin, \$libaa);

    my @Alu_table = ($libaa =~m/ lu_table_template ($parenth_name )$braces /sixg);
#    print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, "."\n";
#    p @Alu_table;
    my $TableName; my $TableContent;
    while ($TableName = shift @Alu_table, $TableContent  = shift @Alu_table) {
        if ($TableContent =~m#index_\d#six){
            my $table_id = ($TableName=~s#[\(\) ]##sixgr);
            $HTlu{$table_id} = $TableContent;
        }
    }
#    p %HTlu;
    my @ACells = ($libaa =~m/ (cell $parenth_name) $braces/sixg);

    my $celli; my $cellTitle;
    while ($cellTitle  =  shift @ACells, $celli =shift @ACells) {
        $cellTitle =~ s#$ws##sixg;

        my @PinBlocks = ( $celli =~ m/pin\s*\("?([^"\(\)]+)"?\)  $braces /sixg );
        my $tblock;
        my $dummy;
        my $pinName;
        my $pblock;
        while ( $pinName = shift @PinBlocks, $pblock = shift @PinBlocks ) {
            my $rH2               = $rH;
            my $rH2min            = $rHmin;
            my $flag_internal_pin = undef;
            if ( $pblock =~ m# \b direction \b [^:]* : $ws "? \b internal \b#six ) {
                $flag_internal_pin = 1;
#                print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, $pinName"."\n";
                $rH2    = $rHi;
                $rH2min = $rHimin;

            } 
#            else {next;}
            my @TimingBlocks = ( $pblock =~ m/(timing $parenth_only $braces)/sixg );

            while ( $tblock = shift @TimingBlocks, shift @TimingBlocks ) {
                if ( $tblock =~ m#min_delay_flag $ws: $ws true#six ) {
                    &TreatTimingBlock( $cellTitle, $fileIndex, $tblock, $rH2min, $pinName );
                } else {
                    &TreatTimingBlock( $cellTitle, $fileIndex, $tblock, $rH2, $pinName );
                }
            }

            if ($check_power) {
                my @PowerBlocks = ( $pblock =~ m/(internal_power $parenth_only $braces)/sixg );
                while ( $tblock = shift @PowerBlocks, shift @PowerBlocks ) {
                    &TreatTimingBlock( $cellTitle, $fileIndex, $tblock, $rH2, $pinName );
                }
            }
        }
    }
}#vim_folder_}}}
 
sub Hash2Csv
{#_{{_
    my ($rH, $CsvF, $FileCount) = @_;
    my $strA=q//;
    if ($flg_TimingModePresence) {
        $strA .= sprintf "#%s,%s,%s,%s,%s,", "cell", "RelatedPin", q{TimingType}, q{Mode}, "pin";
    } else {
        $strA .= sprintf "#%s,%s,%s,%s,", "cell", "RelatedPin", q{TimingType}, "pin";
    }
    foreach my $t1 (@Tassert){
        foreach my $fi (0..$FileCount ){
            my $fn = "f".$fi;
            my $hstring = $t1."-".$fn ;
            $strA .= sprintf "%s,", $hstring;
        }
    }

    $strA.= "\n";

    foreach my $k1 (sort keys %{$rH}){
        my ($k1_new) = ($k1=~m/cell\s*\(\s*"?([^ \(\)"]+)"?\s*\)/);
        my  @Asorted_k1keys2          ;     
        foreach my $k2i   ( keys %{ $rH->{$k1} }){
            my @Al;
            push @Al, $k2i ;
            my $related_string = ($k2i =~m/[^,\[\]]+? (?= (?:,|\[|_checkpin|_\d+))/sixg)[0] ;
            my $pin_string = ($k2i =~/(?<= ->) [^\[\]]+  /sixg)[0];
            $related_string = $related_string // "";
            $pin_string = $pin_string // "";
            push  @Al,  $pin_string, $related_string; 
            my ($num1) = ($k2i =~m/\[(\d+)\].*?->/);
            my ($num2) = ($k2i =~m/->.*?\[(\d+)\]/);
            if ($num1) {push @Al, $num1;} else {push @Al, -1;}
            if ($num2) {push @Al, $num2;} else {push @Al, -1;}
            my ($TType) = ($k2i =~m/,([^-]+)->/);
            $TType = $TType // q();
            push @Al, $TType;
            push @Asorted_k1keys2, \@Al ;
        }

        my  @Asorted_k1keys          = map {$_ ->[0]}
        sort { $a->[1] cmp $b->[1]
            or 
            $a->[4] <=> $b->[4]
            or 
            $a->[2] cmp $b->[2]
            or 
            $a->[3] <=> $b->[3]
            or 
            $a->[5] cmp $b->[5]
        } @Asorted_k1keys2;



        foreach my $k2 (@Asorted_k1keys) {
            my $k2_split = ($k2=~s/->/,/r);
            if ($opt_clock_underscore_number_collpased){
                #------------------------------------------------------
                # to remove the _1 from clock_1; it's same clock for different ports.
                ##------------------------------------------------------
                $k2_split=~s#_\d+,#,#;
            }

            $strA .= sprintf "%s,%s,", $k1_new, $k2_split;
            foreach my $k31 (@Tassert){
                if ($rH->{$k1}{$k2}{$k31}){
                    foreach my $fi (0..$FileCount ){
                        my $k4 = "file$fi";
                        if ($rH->{$k1}{$k2}{$k31}{$k4} and $rH->{$k1}{$k2}{$k31}{$k4}{value}){
                        $strA .= sprintf "%s,", $rH->{$k1}{$k2}{$k31}{$k4}{value};
                        } else { $strA .= qq/-,/; } 
                    }
                } else { 
                    foreach my $fi (0..$FileCount ){
                    $strA .= q/-,/ ;
                    }
                }
            }
            $strA .= qq/\n/;
        }
    }
    &ZipSlurp::writeZF( \$strA, $CsvF);
}#_}}_




sub TreatTimingBlock 
{#_{{_
    my ($cellName, $CornerString, $TimingStringIn, $rHb, $pinName) = @_;
    my ($timing_when) = "";
    my @Aback;
    my $timing_mode = q//;
    ($timing_mode) = ($TimingStringIn =~m/\b mode $parentheses/six);
    my $mode_str = q//;
    if ($timing_mode) {
        $flg_TimingModePresence = 1;
        $timing_mode =~ s#$ws##sixg;
        ($mode_str) = ( $timing_mode =~ m#\(  "? \w+ "? , "? ([^"\(\) \t]+)"?    \)#six );
    }
    ($timing_when) = ($TimingStringIn =~m/\b when \s* : \s* "?([^";]+)"? \s* ;/six);
    if ($timing_when){ return ;} # to ignore the logic-specific assertions
    my ($timing_related_pin) = "";
    ($timing_related_pin) = ($TimingStringIn =~m/\b related_pin \s* : \s* "?([^";]+)"? \s*; /six);
    #------------------------------------------------------
    # to remove the ending _1, _2 etc attributes, which are due to the same clock defined on multiple ports
    ##------------------------------------------------------
    my ($timing_type) = ($TimingStringIn =~m/\b timing_type $ws : $ws "?([^"; ]+)"? \s*; /six);
    $timing_type = ($timing_type)? $timing_type: "?";
    my @ttA = split /_/, $timing_type;
    my $timing_type2 = "";
    foreach my $ai (@ttA){ $timing_type2.= ucfirst $ai; }

    $timing_related_pin.= ",$timing_type2";
    if ($flg_TimingModePresence){
        $timing_related_pin.= ",$mode_str";
    }


    my @ArfBlocks = ($TimingStringIn =~m/  $RiseFallPatterns /sixg);
#    print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, "."\n";
#    p @ArfBlocks;
    my $rfb= ""; my $rftype= ""; my $table_string= "";
    my $IndexPatterns = qr{index_\d+ $parentheses}six;
    while (  $rftype = shift @ArfBlocks, $table_string = shift @ArfBlocks, $rfb= shift @ArfBlocks){#_{{_
        if ($rfb =~m# sigma_type $ws: $ws "? early "? #six){ 
            next;
        }
        my ($values_string) = ($rfb =~/$ValuesPatterns/);
        my @Avs = ($values_string =~m/[^ \t\n\\,\(\)"]+/sigx);
        my $pin_string = $timing_related_pin."->".$pinName;
        if ($table_string =~m#\b scalar \b#six) {
            $rHb->{$cellName}{$pin_string}{$rftype}{$CornerString}{value} = sprintf "%.1f", $Avs[0];
            return;
        }
        my ($In1) = ($rfb =~/index_1 $parentheses/six);
        my ($In2) = ($rfb =~/index_2 $parentheses/six);
        my ($In3) = ($rfb =~/index_3 $parentheses/six);
        my @AIn1; my @AIn2; 
        if (!$In1 and !$In2){
            $table_string =~s#[\(\) ]##sigx;
            if ($HTlu{$table_string}){
                ($In1) =($HTlu{$table_string}=~/index_1 $parentheses/six);
                ($In2) =($HTlu{$table_string}=~/index_2 $parentheses/six);
            } else { next; }
        } elsif ($In1 and !$In2) {
            next;
        } elsif (!$In1 and $In2) {
            next;
        }
        @AIn1 = ($In1 =~m/[^ \t\n\\,\(\)"]+/sigx);
        @AIn2 = ($In2 =~m/[^ \t\n\\,\(\)"]+/sigx);
        my @AIn3;
        if ($In3){ @AIn3 = ($In3 =~m/[^ \t\n\\,\(\)"]+/sigx); 
#            p @AIn3;
        }

        if ( $In3) {#_{{_
            my @Av2 = @Avs; 
            my $R1In1 =$#AIn1-1 ; my $R2In1 =$#AIn1;
            foreach my $i (0..($#AIn1-1) ){
                if ($AIn1[$i] <= $idx1 and $idx1 < $AIn1[$i+1]){
                    $R1In1 = $i; $R2In1 = $i+1; 
                    last;
                }
            }

            my $i = 0;
            while ($i < $R1In1){
                splice @Av2, 0, ($#AIn2+1)*($#AIn3+1);
                $i++;
            }
            my @ADim1R1 = splice @Av2, 0, ($#AIn2+1)*($#AIn3+1);
            my @ADim1R2 = splice @Av2, 0, ($#AIn2+1)*($#AIn3+1);;

#------------------------------------------------------
# The two rails on Dimension-1
##------------------------------------------------------
            my $R1In2 =$#AIn2-1 ; my $R2In2 =$#AIn2;
            foreach my $i (0..($#AIn2-1) ){
                if ($AIn2[$i] <= $idx2 and $idx2 < $AIn2[$i+1]){
                    $R1In2 = $i; $R2In2 = $i+1; 
                    last;
                }
            }

            $i = 0;
            while ($i < $R1In2){
                splice @ADim1R1, 0, ($#AIn3+1);
                splice @ADim1R2, 0, ($#AIn3+1);
                $i++;
            }

            my @ADim2R1_1 = splice @ADim1R1, 0, ($#AIn3+1);
            my @ADim2R1_2 = splice @ADim1R1, 0, ($#AIn3+1);
            my @ADim2R2_1 = splice @ADim1R2, 0, ($#AIn3+1);
            my @ADim2R2_2 = splice @ADim1R2, 0, ($#AIn3+1);

#            print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, "."\n";
#            p @ADim2R1_1;
#            p @ADim2R1_2;
#            p @ADim2R2_1;
#            p @ADim2R2_2;

            my $R1vi1_1 = robust_interpolate($idx3, \@AIn3, \@ADim2R1_1);
            my $R2vi1_2 = robust_interpolate($idx3, \@AIn3, \@ADim2R1_2);
            my $R1vi2_1 = robust_interpolate($idx3, \@AIn3, \@ADim2R2_1);
            my $R2vi2_2 = robust_interpolate($idx3, \@AIn3, \@ADim2R2_2);

#            print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, $R1vi1_1;$R2vi1_2;$R1vi2_1;$R2vi2_2"."\n";
            
            my @Dim2Ix = ($AIn2[$R1In2], $AIn2[$R2In2]);
            my @Dim2Vu1 = ($R1vi1_1, $R2vi1_2);
            my @Dim2Vu2 = ($R1vi2_1, $R2vi2_2);

            my $final_viDim2_1 = robust_interpolate($idx2, \@Dim2Ix, \@Dim2Vu1);
            my $final_viDim2_2 = robust_interpolate($idx2, \@Dim2Ix, \@Dim2Vu2);
#            print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, $final_viDim2_1,$final_viDim2_2"."\n";

            my @Dim1Ix = ($AIn1[$R1In1], $AIn1[$R2In1]);
            my @Dim1Vu = ($final_viDim2_1,$final_viDim2_2);
            my $final_vi = robust_interpolate($idx1, \@Dim1Ix, \@Dim1Vu);


#------------------------------------------------------
# remember to take it out
##------------------------------------------------------
#            $final_vi = 0;
#        print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, $final_vi"."\n";
            $rHb->{$cellName}{$pin_string}{$rftype}{$CornerString}{value} = sprintf "%.1f", $final_vi;

        }#_}}_
        else {# no dimension-3{{
            my @Av2 = @Avs; 
            my $R1In1 =$#AIn1-1 ; my $R2In1 =$#AIn1;
            foreach my $i (0..($#AIn1-1) ){
                if ($AIn1[$i] <= $idx1 and $idx1 < $AIn1[$i+1]){
                    $R1In1 = $i; $R2In1 = $i+1; 
                    last;
                }
            }

            my $i = 0;
            while ($i < $R1In1){
                splice @Av2, 0, ($#AIn2+1);
                $i++;
            }
#------------------------------------------------------
# The two rails on Dimension-1
##------------------------------------------------------
            my @ADim1R1 = splice @Av2, 0, ($#AIn2+1);
            my @ADim1R2 = splice @Av2, 0, ($#AIn2+1);
#------------------------------------------------------
# The Dim-1 interpolation of two rails
##------------------------------------------------------
            my $R1vi;
            my $R2vi;
            if ( $#AIn2 == 0 and $#ADim1R1 == 0 ) {
                $R1vi = constant_interpolate( $idx2, \@AIn2, \@ADim1R1 );
                $R2vi = constant_interpolate( $idx2, \@AIn2, \@ADim1R2 );
            } else {
                $R1vi = robust_interpolate( $idx2, \@AIn2, \@ADim1R1 );
                $R2vi = robust_interpolate( $idx2, \@AIn2, \@ADim1R2 );
            }
            my @Dim1Ix = ($AIn1[$R1In1], $AIn1[$R2In1]);
            my @Dim1Vu = ($R1vi,  $R2vi);
#            print "### {"; print  __FILE__ ; print  "}-{" ; print __LINE__ ; print "}, "."\n";
#            p @Dim1Ix;
#            p @Dim1Vu;
            my $final_vi = robust_interpolate($idx1, \@Dim1Ix, \@Dim1Vu);
            $rHb->{$cellName}{$pin_string}{$rftype}{$CornerString}{value} = sprintf "%.1f", $final_vi;

        }#_}}_
    }#_}}_

}#_}}_


sub print_usage
{#_{{_

    my $strHelp = <<"EOHelp";

## USAGE ##

 $RealBin/$RealScript strp1   

#

strp2;

strp3b
EOHelp



    my $strHelpO2 = <<"EOHelpO2";

-Usage- -out            # optional : output directory;                             
                        #                                                          
-Usage- -lvf            # optional : to also check lvf ocv_sigma tokens,           
-Usage- -ocv            #            works the same as -ocv.                       
                        #                                                          
                        # ocv_sigma tokens including                               
                        #
                        #    ocv_sigma_cell_fall                                   
                        #    ocv_sigma_cell_rise                                   
                        #    ocv_sigma_fall_transition                             
                        #    ocv_sigma_rise_transition                             
                        #                                                          
-Usage- -power          # optional : to check internal_power assertions;           
                        #                                                          
-Usage- -collapse_clock # optional : to remove _1 _2 from clk_1, clk_2 string;     
                        # they are for same clock but different ports;             
                        #                                                                                                                       
-Usage- -index 30 50    # Optional : the index to sample the 2-Dimensional (sometimes 
                        # 3-Dimensional) value blocks. Default is (20, 20) for 2-D.
                        #
                        # A typical lib file would use index-1 for
                        # input-transition time (in pico senconds), and
                        # index-2 for output capacitance load (femto Farad).
                        #
                        # Usage example: 
                        #
                        #   -index 30 50
                        #
                        # For index-1 as 30 and index-2 as 50
                        #

EOHelpO2

$strHelp =~s#\b strp1 \b #sprintf("%s", CONm(qq(-libs <lib1_name> <lib2_name> <lib3_name> ...etc), 5))#sixe;
$strHelp =~s#\b strp2 \b #sprintf("%s", CONm(qq(-Usage- -libs           : Required : Full path to ALL lib files), 5))#sixe;
                
$strHelp =~s#\b strp3b \b #sprintf("%s", CONm($strHelpO2, 8))#sixe;

 
    say $strHelp;

}#_}}_

